from typing import Literal

import numpy as np
import pandas as pd

from ..geocal.pileCapacityCal import PileCapacityCal, CompositePileCapacityCal
from ..dataclass.geoStructures import PileType, BoredPile, PostGroutedPile, ConcreteSquarePile, SteelPipePile, ConcreteHollowPile, RockSocketedPile, FlexiblePile, RigidPile
from ..pipeline.pipeline import status_manage, PipeModule, Port
from ..pipeline.portTypes import PortTypeHint, PortType
from ..pipeline.pipeData import RangeModel
from ..dataclass.terminologies import Units, GeoResults, GeoMaterialProps
from ..dataclass.tables import FieldMetadata, TableCollection, TableData
from ..dataclass.results import SingleResult, UnitResult


@status_manage
class CreatePile(PipeModule):
    """Create a pile"""

    def __init__(self, mname: str = "CreatePile", auto_run: bool = True,
                 name: str | None = None, top: float = 0.0, length: float = 10.0, 
                 design_ground_elev: float = 0.0) -> None:
        """Create a pile.
        
        Parameters
        ----------
        name: str, default: None
            The name of the pile.

        top (m): float, default: 0.0
            The top of the pile.

        length (m): float, default: 10.0
            The length of the pile.

        design_ground_elev (m): float, default: 0.0
            The elevation of the design ground.
        """
        super().__init__(mname=mname, auto_run=auto_run)

        self._ports_out = {"OutputPile": Port(ptype=PortType.Pile, pdoc="The output pile object.")}

        self.name = name
        self.top = top
        self.length = length
        self.design_ground_elev = design_ground_elev
        self.pile_type: PileType | None = None

        # Bored pile
        self.diameter: float = 0.8

        # Post grouted pile
        self.post_grouted_formation: Literal["mud_wall", "dry_work"] = "mud_wall"
        self.grout_position: Literal["pile_base", "base_and_skin"] = "pile_base"

        # Concrete square pile
        self.width: float = 0.8

        # Steel pipe pile
        self.outer_diameter: float = 1.2
        self.section_type: Literal["full_open", "half_open", "full_closed"] = "full_open"
        self.num_partition: Literal[2, 4, 9] = 2

        # Concrete hollow pile 
        self.hollow_outer_diameter: float = 1.2
        self.inner_diameter: float = 0.8
        self.section_shape: Literal["circular", "square"] = "circular"

        # Rock socketed pile
        self.rock_diameter: float = 1.2
        self.rock_width: float = 0.8
        self.rock_section_shape: Literal["circular", "square", "rectangular"] = "circular"
        self.formation: Literal["mud_wall", "dry_work", "mud_wall_then_grout"] = "mud_wall"

    @property
    def OutputPile(self) -> PortTypeHint.Pile | None:
        return self["OutputPile"]
    
    def create_bored_pile(self, diameter: float = 0.8) -> None:
        """Create a bored pile.
        
        Parameters
        ----------
        diameter (m): float, default: 0.8
            The diameter of the pile.
        """
        self.pile_type = PileType.BoredPile
        self.diameter = diameter

    def create_post_grouted_pile(self, diameter: float = 0.8, formation: Literal["mud_wall", "dry_work"] = "mud_wall",
                                  grout_position: Literal["pile_base", "base_and_skin"] = "pile_base") -> None:
        """Create a post grouted pile.
        
        Parameters
        ----------
        diameter (m): float, default: 0.8
            The diameter of the pile.

        formation: Literal["mud_wall", "dry_work"], default: "mud_wall"
            The formation of the pile.
            mud_wall: 泥浆护壁成孔
            dry_work: 干作业成孔

        grout_position: Literal["pile_base", "base_and_skin"], default: "pile_base"
            The position of the grout.
            pile_base: 单一桩端注浆
            base_and_skin: 桩端桩侧复式注浆
        """
        self.pile_type = PileType.PostGroutedPile
        self.diameter = diameter
        self.post_grouted_formation = formation
        self.grout_position = grout_position

    def create_concrete_square_pile(self, width: float = 0.8) -> None:
        """Create a concrete square pile.
        
        Parameters
        ----------
        width (m): float, default: 0.8
            The width of the pile.
        """
        self.pile_type = PileType.ConcreteSquarePile
        self.width = width

    def create_steel_pipe_pile(self, outer_diameter: float = 0.8, section_type: Literal["full_open", "half_open", "full_closed"] = "full_open",
                               num_partition: Literal[2, 4, 9] = 2) -> None:
        """Create a steel pipe pile.
        
        Parameters
        ----------
        outer_diameter (m): float, default: 0.8
            The outer diameter of the pile.

        section_type: Literal["full_open", "half_open", "full_closed"], default: "full_open"
            The section type of the pile.
            full_open: 全开口
            half_open: 半开口
            full_closed: 全封闭

        num_partition: Literal[2, 4, 9], default: 2
            The number of partitions of the pile.
            Only available when the section_type is "half_open".
        """
        self.pile_type = PileType.SteelPipePile
        self.outer_diameter = outer_diameter
        self.section_type = section_type
        self.num_partition = num_partition

    def create_concrete_hollow_pile(self, outer_diameter: float = 0.8, inner_diameter: float = 0.6,
                                    section_shape: Literal["circular", "square"] = "circular") -> None:
        """Create a concrete hollow pile.
        
        Parameters
        ----------
        outer_diameter (m): float, default: 0.8
            The outer diameter of the pile.

        inner_diameter (m): float, default: 0.6
            The inner diameter of the pile.

        section_shape: Literal["circular", "square"], default: "circular"
            The section shape of the pile.
        """
        self.pile_type = PileType.ConcreteHollowPile
        self.hollow_outer_diameter = outer_diameter
        self.inner_diameter = inner_diameter
        self.section_shape = section_shape

    def create_rock_socketed_pile(self, diameter: float = 0.8, width: float = 0.8,
                                  section_shape: Literal["circular", "square"] = "circular",
                                  formation: Literal["mud_wall", "dry_work", "mud_wall_then_grout"] = "mud_wall") -> None:
        """Create a rock socketed pile.
        
        Parameters
        ----------
        diameter (m): float, default: 0.8
            The diameter of the pile.

        width (m): float, default: 0.8
            The width of the pile.

        section_shape: Literal["circular", "square"], default: "circular"
            The section shape of the pile.

        formation: Literal["mud_wall", "dry_work", "mud_wall_then_grout"], default: "mud_wall"
            The formation of the pile.
            mud_wall: 泥浆护壁成孔
            dry_work: 干作业成孔
            mud_wall_then_grout: 泥浆护壁成孔后注浆
        """
        self.pile_type = PileType.RockSocketedPile
        self.rock_diameter = diameter
        self.rock_width = width
        self.rock_section_shape = section_shape
        self.formation = formation

    def set_cal_params(self, reset: bool = True) -> dict[str, RangeModel]:
        diameter_required = False
        post_grouted_formation_required = False
        grout_position_required = False
        width_required = False
        outer_diameter_required = False
        section_type_required = False
        num_partition_required = False
        hollow_outer_diameter_required = False
        inner_diameter_required = False
        section_shape_required = False
        rock_diameter_required = False
        rock_width_required = False
        rock_section_shape_required = False
        formation_required = False

        if self.pile_type == PileType.BoredPile:
            diameter_required = True
        elif self.pile_type == PileType.PostGroutedPile:
            diameter_required = True
            post_grouted_formation_required = True
            grout_position_required = True
        elif self.pile_type == PileType.ConcreteSquarePile:
            width_required = True
        elif self.pile_type == PileType.SteelPipePile:
            outer_diameter_required = True
            section_type_required = True
            if self.section_type == "half_open":
                num_partition_required = True
            else:
                num_partition_required = False
        elif self.pile_type == PileType.ConcreteHollowPile:
            hollow_outer_diameter_required = True
            inner_diameter_required = True
            section_shape_required = True
        elif self.pile_type == PileType.RockSocketedPile:
            rock_diameter_required = True
            rock_section_shape_required = True
            formation_required = True
            if self.rock_section_shape == "rectangular":
                rock_width_required = True

        pile_type_choices = [PileType.BoredPile, PileType.PostGroutedPile, PileType.ConcreteSquarePile, PileType.SteelPipePile, 
                             PileType.ConcreteHollowPile, PileType.RockSocketedPile]
        pile_type_choices_label = [PileType.BoredPile.title, PileType.PostGroutedPile.title, PileType.ConcreteSquarePile.title, 
                                   PileType.SteelPipePile.title, PileType.ConcreteHollowPile.title, PileType.RockSocketedPile.title]
        
        values_range = {"pile_type": RangeModel(vtype="str", title="桩型", default=self.pile_type, choices=pile_type_choices,
                                                choices_label=pile_type_choices_label),
                        "name": RangeModel(vtype="str", title="桩基名称", default=self.name),
                        "top": RangeModel(vtype="float", title="桩顶标高", default=self.top, units=Units.m),
                        "length": RangeModel(vtype="float", title="桩长", default=self.length, units=Units.m, minmax=(0, None), include_min=False),
                        "design_ground_elev": RangeModel(vtype="float", title="设计地面标高", default=self.design_ground_elev, units=Units.m),
                        "diameter": RangeModel(vtype="float", title="桩径", default=self.diameter, units=Units.m, minmax=(0, None), include_min=False,
                                               depends_on="pile_type", required=diameter_required, visible=diameter_required),
                        "post_grouted_formation": RangeModel(vtype="str", title="成桩工艺", default=self.post_grouted_formation, 
                                                             choices=["mud_wall", "dry_work"], choices_label=["泥浆护壁成孔", "干作业成孔"],
                                                             depends_on="pile_type",
                                                             required=post_grouted_formation_required,
                                                             visible=post_grouted_formation_required),
                        "grout_position": RangeModel(vtype="str", title="注浆位置", default=self.grout_position, 
                                                     choices=["pile_base", "base_and_skin"], choices_label=["单一桩端注浆", "桩端桩侧复式注浆"],
                                                     depends_on="pile_type",
                                                     required=grout_position_required,
                                                     visible=grout_position_required),
                        "width": RangeModel(vtype="float", title="桩宽", default=self.width, units=Units.m, minmax=(0, None), include_min=False,
                                            depends_on="pile_type", required=width_required, visible=width_required),
                        "outer_diameter": RangeModel(vtype="float", title="桩外径", default=self.outer_diameter, units=Units.m, minmax=(0, None), include_min=False,
                                                     depends_on="pile_type", required=outer_diameter_required, visible=outer_diameter_required),
                        "section_type": RangeModel(vtype="str", title="桩截面类型", default=self.section_type, 
                                                   choices=["full_open", "half_open", "full_closed"],
                                                   choices_label=["完全敞口", "半敞口", "完全闭口"],
                                                   depends_on="pile_type",
                                                   required=section_type_required,
                                                   visible=section_type_required),
                        "num_partition": RangeModel(vtype="int", title="分割块数", default=self.num_partition, choices=[2, 4, 9],
                                                     depends_on=["pile_type", "section_type"], 
                                                     required=num_partition_required, visible=num_partition_required),
                        "hollow_outer_diameter": RangeModel(vtype="float", title="桩外径/边长", default=self.hollow_outer_diameter, units=Units.m, 
                                                            minmax=(0, None), include_min=False,
                                                            depends_on="pile_type", 
                                                            required=hollow_outer_diameter_required, 
                                                            visible=hollow_outer_diameter_required),
                        "inner_diameter": RangeModel(vtype="float", title="桩内径", default=self.inner_diameter, units=Units.m, 
                                                     minmax=(0, None), include_min=False,
                                                     depends_on="pile_type", 
                                                     required=inner_diameter_required, 
                                                     visible=inner_diameter_required),
                        "section_shape": RangeModel(vtype="str", title="桩截面形状", default=self.section_shape, 
                                                    choices=["circular", "square"],
                                                    choices_label=["圆形", "方形"], 
                                                    depends_on="pile_type", 
                                                    required=section_shape_required, 
                                                    visible=section_shape_required),
                        "formation": RangeModel(vtype="str", title="成桩工艺", default=self.formation, 
                                                choices=["mud_wall", "dry_work", "mud_wall_then_grout"],
                                                choices_label=["泥浆护壁成桩", "干作业成桩", "泥浆护壁成桩后注浆"],
                                                depends_on="pile_type",
                                                required=formation_required,
                                                visible=formation_required),
                        "rock_diameter": RangeModel(vtype="float", title="桩径/边长", default=self.rock_diameter, units=Units.m, 
                                                     minmax=(0, None), include_min=False,
                                                     depends_on="pile_type",
                                                     required=rock_diameter_required,
                                                     visible=rock_diameter_required),
                        "rock_width": RangeModel(vtype="float", title="桩宽", default=self.rock_width, units=Units.m, 
                                                 minmax=(0, None), include_min=False,
                                                 depends_on=["pile_type", "rock_section_shape"],
                                                 required=rock_width_required,
                                                 visible=rock_width_required),
                        "rock_section_shape": RangeModel(vtype="str", title="桩截面形状", default=self.rock_section_shape, 
                                                        choices=["circular", "square", "rectangular"],
                                                        choices_label=["圆形", "方形", "矩形"],
                                                        depends_on="pile_type",
                                                        required=rock_section_shape_required,
                                                        visible=rock_section_shape_required)
                        }
        return values_range
            
    def execute(self) -> PortTypeHint.Pile | None:
        if self.name is None or self.pile_type is None:
            self._ports_out["OutputPile"].data = None
            return None

        if self.pile_type == PileType.BoredPile:
            pile = BoredPile(name=self.name, top=self.top, length=self.length, design_ground_elev=self.design_ground_elev,
                             diameter=self.diameter)
        elif self.pile_type == PileType.PostGroutedPile:
            pile = PostGroutedPile(name=self.name, top=self.top, length=self.length, design_ground_elev=self.design_ground_elev,
                                   diameter=self.diameter, formation=self.post_grouted_formation,
                                   grout_position=self.grout_position)
        elif self.pile_type == PileType.ConcreteSquarePile:
            pile = ConcreteSquarePile(name=self.name, top=self.top, length=self.length, design_ground_elev=self.design_ground_elev,
                                      width=self.width)
        elif self.pile_type == PileType.SteelPipePile:
            pile = SteelPipePile(name=self.name, top=self.top, length=self.length, design_ground_elev=self.design_ground_elev,
                                 outer_diameter=self.outer_diameter, section_type=self.section_type, num_partition=self.num_partition)
        elif self.pile_type == PileType.ConcreteHollowPile:
            pile = ConcreteHollowPile(name=self.name, top=self.top, length=self.length, design_ground_elev=self.design_ground_elev,
                                      outer_diameter=self.hollow_outer_diameter, inner_diameter=self.inner_diameter, 
                                      section_shape=self.section_shape)
        elif self.pile_type == PileType.RockSocketedPile:
            pile = RockSocketedPile(name=self.name, top=self.top, length=self.length, design_ground_elev=self.design_ground_elev,
                                    diameter=self.rock_diameter, width=self.rock_width, section_shape=self.rock_section_shape,
                                    formation=self.formation)
            
        self._ports_out["OutputPile"].data = pile
        return pile
    

@status_manage
class CreateCompositePile(PipeModule):
    """Create a composite pile"""

    def __init__(self, mname: str = "CreateCompositePile", auto_run: bool = True,
                 name: str | None = None, diameter: float = 0.5, charac_composite_soil_bc: float = 100.0,
                 pile_layout: Literal["triangle", "square", "rectangular"] = "triangle",
                 pile_space: float = 1.0, longitudinal_space: float = 1.0, lateral_space: float = 1.2) -> None:
        """Create a composite pile.
        
        Parameters
        ----------
        name: str | None, default: None
            The name of the pile.

        diameter (m): float, default: 0.5
            The diameter of the pile.

        charac_composite_soil_bc (kPa): float, default: 100.0
            The characteristic composite soil bearing capacity.

        pile_layout: Literal["triangle", "square", "rectangular"], default: "triangle"
            The layout of the pile.
            triangle: 等边三角形
            square: 正方形
            rectangular: 矩形

        pile_space (m): float, default: 1.0
            The space between the piles.
            Only available when the pile_layout is "triangle" or "square".

        longitudinal_space (m): float, default: 1.0
            The longitudinal space between the piles.
            Only available when the pile_layout is "rectangular".

        lateral_space (m): float, default: 1.2
            The lateral space between the piles.
            Only available when the pile_layout is "rectangular".
        """
        super().__init__(mname=mname, auto_run=auto_run)

        self._ports_out = {"OutputPile": Port(ptype=PortType.CompositePile, pdoc="The output composite pile object.")}

        self.name = name
        self.diameter = diameter
        self.charac_composite_soil_bc = charac_composite_soil_bc
        self.pile_layout = pile_layout
        self.pile_space = pile_space
        self.longitudinal_space = longitudinal_space
        self.lateral_space = lateral_space

        self.pile_type: PileType | None = None

        # Flexible pile
        self.pile_soil_stress_ratio: float = 1.5

        # Rigid pile
        self.top: float = 0.0
        self.length: float = 10.0
        self.design_ground_elev: float = 0.0
        self.pile_bc_factor: float = 1.2
        self.composite_soil_bc_factor: float = 1.2
        self.base_resistance_factor: float = 1.2

    @property
    def OutputPile(self) -> PortTypeHint.CompositePile | None:
        return self["OutputPile"]
    
    def create_flexible_pile(self, pile_soil_stress_ratio: float = 1.5) -> None:
        """Create a flexible pile.
        
        Parameters
        ----------
        pile_soil_stress_ratio: float, default: 1.5
            The pile soil stress ratio. 桩土应力比
        """
        self.pile_type = PileType.FlexiblePile
        self.pile_soil_stress_ratio = pile_soil_stress_ratio
    
    def create_rigid_pile(self, top: float = 0.0, length: float = 10.0, design_ground_elev: float = 0.0,
                         pile_bc_factor: float = 1.2, composite_soil_bc_factor: float = 1.2, base_resistance_factor: float = 1.2) -> None:
        """Create a rigid pile.
        
        Parameters
        ----------
        top (m): float, default: 0.0
            The top of the pile.

        length (m): float, default: 10.0
            The length of the pile.

        design_ground_elev (m): float, default: 0.0
            The design ground elevation.

        pile_bc_factor: float, default: 1.2,
            The pile bearing capacity factor. 单桩承载力发挥系数, lambda

        composite_soil_bc_factor: float, default: 1.2
            The composite soil bearing capacity factor. 桩间土承载力发挥系数, beta

        base_resistance_factor: float, default: 1.2
            The base resistance factor. 端阻力发挥系数, alpha_p
        """
        self.pile_type = PileType.RigidPile
        self.top = top
        self.length = length
        self.design_ground_elev = design_ground_elev
        self.pile_bc_factor = pile_bc_factor
        self.composite_soil_bc_factor = composite_soil_bc_factor
        self.base_resistance_factor = base_resistance_factor

    def set_cal_params(self, reset: bool = True) -> dict[str, RangeModel]:
        pile_soil_stress_ratio_requied = False
        top_requied = False
        length_requied = False
        design_ground_elev_requied = False
        pile_bc_factor_requied = False
        composite_soil_bc_factor_requied = False
        base_resistance_factor_requied = False

        pile_space_requied = False
        longitudinal_space_requied = False
        lateral_space_requied = False

        if self.pile_type == PileType.FlexiblePile:
            pile_soil_stress_ratio_requied = True
        elif self.pile_type == PileType.RigidPile:
            top_requied = True
            length_requied = True
            design_ground_elev_requied = True
            pile_bc_factor_requied = True
            composite_soil_bc_factor_requied = True
            base_resistance_factor_requied = True

        if self.pile_layout == "triangle" or self.pile_layout == "square":
            pile_space_requied = True
        elif self.pile_layout == "rectangular":
            longitudinal_space_requied = True
            lateral_space_requied = True

        pile_type_choices = [PileType.FlexiblePile, PileType.RigidPile]
        pile_type_choices_label = [PileType.FlexiblePile.title, PileType.RigidPile.title]

        pile_layout_choices = ["triangle", "square", "rectangular"]
        pile_layout_choices_label = ["等边三角形", "正方形", "矩形"]

        values_range = {
            "name": RangeModel(vtype="str", title="桩基名称", default=self.name),
            "diameter": RangeModel(vtype="float", title="桩径", default=self.diameter, units=Units.m),
            "charac_composite_soil_bc": RangeModel(vtype="float", title="处理后桩间土承载力特征值", default=self.charac_composite_soil_bc, units=Units.kPa),
            "pile_layout": RangeModel(vtype="str", title="布桩方式", default=self.pile_layout, choices=pile_layout_choices, choices_label=pile_layout_choices_label),
            "pile_space": RangeModel(vtype="float", title="桩间距", default=self.pile_space, units=Units.m, depends_on="pile_layout", 
                                     required=pile_space_requied, visible=pile_space_requied),
            "longitudinal_space": RangeModel(vtype="float", title="纵向桩间距", default=self.longitudinal_space, units=Units.m, depends_on="pile_layout",
                                             required=longitudinal_space_requied, visible=longitudinal_space_requied),
            "lateral_space": RangeModel(vtype="float", title="横向桩间距", default=self.lateral_space, units=Units.m, depends_on="pile_layout",
                                        required=lateral_space_requied, visible=lateral_space_requied),
            "pile_type": RangeModel(vtype="str", title="桩型", default=self.pile_type, choices=pile_type_choices, choices_label=pile_type_choices_label),
            "pile_soil_stress_ratio": RangeModel(vtype="float", title="桩土应力比", default=self.pile_soil_stress_ratio, depends_on="pile_type",
                                                 required=pile_soil_stress_ratio_requied, visible=pile_soil_stress_ratio_requied),
            "top": RangeModel(vtype="float", title="桩顶标高", default=self.top, units=Units.m, depends_on="pile_type",
                              required=top_requied, visible=top_requied),
            "length": RangeModel(vtype="float", title="桩长", default=self.length, units=Units.m, depends_on="pile_type",
                                 required=length_requied, visible=length_requied),
            "design_ground_elev": RangeModel(vtype="float", title="设计地面标高", default=self.design_ground_elev, units=Units.m, depends_on="pile_type",
                                             required=design_ground_elev_requied, visible=design_ground_elev_requied),
            "pile_bc_factor": RangeModel(vtype="float", title="单桩承载力发挥系数\\lambda", default=self.pile_bc_factor, depends_on="pile_type",
                                         required=pile_bc_factor_requied, visible=pile_bc_factor_requied),
            "composite_soil_bc_factor": RangeModel(vtype="float", title="桩间土承载力发挥系数\\beta", default=self.composite_soil_bc_factor, depends_on="pile_type",
                                                   required=composite_soil_bc_factor_requied, visible=composite_soil_bc_factor_requied),
            "base_resistance_factor": RangeModel(vtype="float", title="桩端端阻力发挥系数\\alpha_p", default=self.base_resistance_factor, depends_on="pile_type",
                                                 required=base_resistance_factor_requied, visible=base_resistance_factor_requied),
        }

        return values_range

    def execute(self) -> PortTypeHint.CompositePile | None:
        if self.name is None or self.pile_type is None:
            self._ports_out["OutputPile"].data = None
            return None
        
        if self.pile_type == PileType.FlexiblePile:
            pile = FlexiblePile(name=self.name, diameter=self.diameter, charac_composite_soil_bc=self.charac_composite_soil_bc,
                                pile_layout=self.pile_layout, pile_space=self.pile_space, longitudinal_space=self.longitudinal_space,
                                lateral_space=self.lateral_space, pile_soil_stress_ratio=self.pile_soil_stress_ratio)
        elif self.pile_type == PileType.RigidPile:
            pile = RigidPile(name=self.name, diameter=self.diameter, charac_composite_soil_bc=self.charac_composite_soil_bc,
                             pile_layout=self.pile_layout, pile_space=self.pile_space, longitudinal_space=self.longitudinal_space,
                             lateral_space=self.lateral_space, top=self.top, length=self.length, design_ground_elev=self.design_ground_elev,
                             pile_bc_factor=self.pile_bc_factor, composite_soil_bc_factor=self.composite_soil_bc_factor,
                             base_resistance_factor=self.base_resistance_factor)

        self._ports_out["OutputPile"].data = pile
        return pile
        

@status_manage
class PileCapacity(PipeModule):
    """Calculate the capacity of a pile"""

    def __init__(self, mname: str = "PileCapacity", auto_run: bool = True,
                 profiles: PortTypeHint.MultiProfile1D | None = None,
                 material_table: PortTypeHint.MaterialTable | None = None,
                 pile: PortTypeHint.Pile | None = None) -> None:
        """Calculate the capacity of a pile.
        
        Parameters
        ----------
        profiles : PortTypeHint.MultiProfile1D, default: None
            The 1D geological profiles.
        """
        super().__init__(mname=mname, auto_run=auto_run)

        self._ports_in = {"InputProfiles": Port(ptype=PortType.MultiProfile1D, data=profiles, pdoc="The 1D geological profiles."),
                          "InputMaterialTable": Port(ptype=PortType.MaterialTable, data=material_table, pdoc="The material table."),
                          "InputPile": Port(ptype=PortType.Pile, data=pile, pdoc="The input pile.")}
        self._ports_out = {"OutputResult": Port(ptype=PortType.TableCollection, pdoc="The output pile capacity result.")}

    @property
    def InputProfiles(self) -> None:
        raise AttributeError("Property 'InputProfiles' is write-only.")
    
    @InputProfiles.setter
    def InputProfiles(self, value: PortTypeHint.MultiProfile1D | None) -> None:
        self["InputProfiles"] = value
    
    @property
    def InputMaterialTable(self) -> None:
        raise AttributeError("Property 'InputMaterialTable' is write-only.")
    
    @InputMaterialTable.setter
    def InputMaterialTable(self, value: PortTypeHint.MaterialTable | None) -> None:
        self["InputMaterialTable"] = value
    
    @property
    def InputPile(self) -> None:
        raise AttributeError("Property 'InputPile' is write-only.")
    
    @InputPile.setter
    def InputPile(self, value: PortTypeHint.Pile | None) -> None:
        self["InputPile"] = value
    
    @property
    def OutputResult(self) -> PortTypeHint.TableCollection | None:
        return self["OutputResult"]
    
    def set_cal_params(self, reset: bool = False) -> None:
        return None
    
    def execute(self) -> PortTypeHint.TableCollection | None:
        profiles: PortTypeHint.MultiProfile1D | None = self._ports_in["InputProfiles"].data
        material_table: PortTypeHint.MaterialTable | None = self._ports_in["InputMaterialTable"].data
        pile: PortTypeHint.Pile | None = self._ports_in["InputPile"].data

        if profiles is None or material_table is None or pile is None:
            self._ports_out["OutputResult"].data = None
            return None
        
        # Calcualte the breaing capacity
        all_skin_capacity = []
        all_base_capacity = []
        profile_nums = []
        for profile in profiles:
            cal_capacity = PileCapacityCal(pile=pile, profile=profile, material_table=material_table)
            skin_capacity = cal_capacity.skin_capacity()
            base_capacity = cal_capacity.base_capacity()
            all_skin_capacity.append(skin_capacity)
            all_base_capacity.append(base_capacity)
            profile_nums.append(profile.pnum)

        # Organize result
        safety_factor = 2
        # Bored pile and Concrete square pile
        if pile.pile_type == PileType.BoredPile or pile.pile_type == PileType.ConcreteSquarePile:
            total_result = {GeoResults.ProfileNumber: [], GeoResults.PileLength: [], GeoResults.PileBearingCapacity: [],
                            GeoResults.SF: [], GeoResults.PileCharacteristicBearingCapacity: []}
            layer_result = {GeoResults.ProfileNumber: [], GeoMaterialProps.LayerNumber: [], GeoMaterialProps.LimitSkinFriction: [],
                            GeoMaterialProps.LimitBaseResistance: [], GeoResults.LayerThickness: []}
            self.__populate_total_result(total_result=total_result, safety_factor=safety_factor, pile_length=pile.length,
                                         profile_nums=profile_nums, all_skin_capacity=all_skin_capacity, all_base_capacity=all_base_capacity)
            self.__populate_layer_result(layer_result=layer_result, material_table=material_table,
                                         profile_nums=profile_nums, all_skin_capacity=all_skin_capacity, all_base_capacity=all_base_capacity)
            # Organize fields metadata
            total_fields_meta = self.__get_total_fields_meta()
            layer_fields_meta = self.__get_layer_fields_meta()

            if pile.pile_type == PileType.BoredPile:
                result_tables = TableCollection(name=PileType.BoredPile, title=PileType.BoredPile.title)
            elif pile.pile_type == PileType.ConcreteSquarePile:
                result_tables = TableCollection(name=PileType.ConcreteSquarePile, title=PileType.ConcreteSquarePile.title)
            
        # Post grouted pile
        elif pile.pile_type == PileType.PostGroutedPile:
            total_result = {GeoResults.ProfileNumber: [], GeoResults.PileLength: [], GeoResults.PileBearingCapacity: [],
                            GeoResults.SF: [], GeoResults.PileCharacteristicBearingCapacity: []}
            layer_result = {GeoResults.ProfileNumber: [], GeoMaterialProps.LayerNumber: [], GeoMaterialProps.LimitSkinFriction: [],
                            GeoMaterialProps.LimitBaseResistance: [], GeoMaterialProps.GroutedSkinFrictionFactor: [],
                            GeoMaterialProps.GroutedBaseResistanceFactor: [], GeoResults.LayerThickness: []}
            
            self.__populate_total_result(total_result=total_result, safety_factor=safety_factor, pile_length=pile.length,
                                    profile_nums=profile_nums, all_skin_capacity=all_skin_capacity, all_base_capacity=all_base_capacity)
            self.__populate_layer_result(layer_result=layer_result, material_table=material_table,
                                    profile_nums=profile_nums, all_skin_capacity=all_skin_capacity, all_base_capacity=all_base_capacity)
            
            # Organize fields metadata
            total_fields_meta = self.__get_total_fields_meta()
            layer_fields_meta = self.__get_layer_fields_meta()
            # Additional fields metadata for post-grouted-pile
            add_layer_fields_meta = [
                FieldMetadata(name=GeoMaterialProps.GroutedSkinFrictionFactor, 
                              title=GeoMaterialProps.GroutedSkinFrictionFactor.title,
                              unit=Units.UNITLESS),
                FieldMetadata(name=GeoMaterialProps.GroutedBaseResistanceFactor,
                              title=GeoMaterialProps.GroutedBaseResistanceFactor.title,
                              unit=Units.UNITLESS)
            ]
            layer_fields_meta[-1:-1] = add_layer_fields_meta

            result_tables = TableCollection(name=PileType.PostGroutedPile, title=PileType.PostGroutedPile.title)
            
        # Steel pipe pile
        elif pile.pile_type == PileType.SteelPipePile:
            total_result = {GeoResults.ProfileNumber: [], GeoResults.PileLength: [], 
                            GeoResults.PileBaseBlockingFactor: [], 
                            GeoResults.PileBearingCapacity: [], GeoResults.SF: [], GeoResults.PileCharacteristicBearingCapacity: []}
            layer_result = {GeoResults.ProfileNumber: [], GeoMaterialProps.LayerNumber: [], GeoMaterialProps.LimitSkinFriction: [],
                            GeoMaterialProps.LimitBaseResistance: [], GeoResults.LayerThickness: []}
            
            self.__populate_total_result(total_result=total_result, safety_factor=safety_factor, pile_length=pile.length,
                                         profile_nums=profile_nums, all_skin_capacity=all_skin_capacity, all_base_capacity=all_base_capacity)
            self.__populate_layer_result(layer_result=layer_result, material_table=material_table,
                                    profile_nums=profile_nums, all_skin_capacity=all_skin_capacity, all_base_capacity=all_base_capacity)
            
            # Organize fields metadata
            total_fields_meta = self.__get_total_fields_meta()
            layer_fields_meta = self.__get_layer_fields_meta()
            # Additional fields metadata for steel pipe pile
            add_total_fields_meta = FieldMetadata(name=GeoResults.PileBaseBlockingFactor,
                                                  title=GeoResults.PileBaseBlockingFactor.title,
                                                  unit=Units.UNITLESS)
            total_fields_meta.insert(2, add_total_fields_meta)

            result_tables = TableCollection(name=PileType.SteelPipePile, title=PileType.SteelPipePile.title)

        # Concrete hollow pile
        elif pile.pile_type == PileType.ConcreteHollowPile:
            total_result = {GeoResults.ProfileNumber: [], GeoResults.PileLength: [], 
                            GeoResults.HollowPileNetArea: [], GeoResults.HollowPileOpenArea: [], GeoResults.PileBaseBlockingFactor: [], 
                            GeoResults.PileBearingCapacity: [], GeoResults.SF: [], GeoResults.PileCharacteristicBearingCapacity: []}
            layer_result = {GeoResults.ProfileNumber: [], GeoMaterialProps.LayerNumber: [], GeoMaterialProps.LimitSkinFriction: [],
                            GeoMaterialProps.LimitBaseResistance: [], GeoResults.LayerThickness: []}
            
            self.__populate_total_result(total_result=total_result, safety_factor=safety_factor, pile_length=pile.length,
                                         profile_nums=profile_nums, all_skin_capacity=all_skin_capacity, all_base_capacity=all_base_capacity)
            self.__populate_layer_result(layer_result=layer_result, material_table=material_table,
                                    profile_nums=profile_nums, all_skin_capacity=all_skin_capacity, all_base_capacity=all_base_capacity)
            
            # Organize fields metadata
            total_fields_meta = self.__get_total_fields_meta()
            layer_fields_meta = self.__get_layer_fields_meta()
            # Additional fields metadata for concrete hollow pile
            add_total_fields_meta = [
                FieldMetadata(name=GeoResults.HollowPileNetArea,
                              title=GeoResults.HollowPileNetArea.title,
                              unit=Units.m2),
                FieldMetadata(name=GeoResults.HollowPileOpenArea,
                              title=GeoResults.HollowPileOpenArea.title,
                              unit=Units.m2),
                FieldMetadata(name=GeoResults.PileBaseBlockingFactor,
                              title=GeoResults.PileBaseBlockingFactor.title,
                              unit=Units.UNITLESS)
            ]
            total_fields_meta[2:2] = add_total_fields_meta

            result_tables = TableCollection(name=PileType.ConcreteHollowPile, title=PileType.ConcreteHollowPile.title)

        # Rock socketed pile
        if pile.pile_type == PileType.RockSocketedPile:
            total_result = {GeoResults.ProfileNumber: [], GeoResults.PileLength: [], 
                            GeoResults.RockPileDepthToDiameter: [], GeoResults.RockPileResistanceFactor: [],
                            GeoResults.PileBearingCapacity: [], GeoResults.SF: [], GeoResults.PileCharacteristicBearingCapacity: []}
            layer_result = {GeoResults.ProfileNumber: [], GeoMaterialProps.LayerNumber: [], GeoMaterialProps.LimitSkinFriction: [],
                            GeoMaterialProps.SaturateUCS: [], GeoResults.LayerThickness: []}
            
            self.__populate_total_result(total_result=total_result, safety_factor=safety_factor, pile_length=pile.length,
                                         profile_nums=profile_nums, all_skin_capacity=all_skin_capacity, all_base_capacity=all_base_capacity)
            self.__populate_layer_result(layer_result=layer_result, material_table=material_table,
                                    profile_nums=profile_nums, all_skin_capacity=all_skin_capacity, all_base_capacity=all_base_capacity)
            
            # Organize fields metadata
            total_fields_meta = self.__get_total_fields_meta()
            layer_fields_meta = self.__get_layer_fields_meta()
            # Additional fields metadata for rock socketed pile
            add_total_fields_meta = [
                FieldMetadata(name=GeoResults.RockPileDepthToDiameter,
                              title=GeoResults.RockPileDepthToDiameter.title,
                              unit=Units.UNITLESS),
                FieldMetadata(name=GeoResults.RockPileResistanceFactor,
                              title=GeoResults.RockPileResistanceFactor.title,
                              unit=Units.UNITLESS)
            ]
            total_fields_meta[2:2] = add_total_fields_meta

            add_layer_fields_meta = FieldMetadata(name=GeoMaterialProps.SaturateUCS,
                                                  title=GeoMaterialProps.SaturateUCS.title,
                                                  unit=Units.MPa)
            layer_fields_meta[3] = add_layer_fields_meta

            result_tables = TableCollection(name=PileType.RockSocketedPile, title=PileType.RockSocketedPile.title)
        
        
        total_table = TableData(data=total_result, name="total_result", fields_meta=total_fields_meta)
        layer_table = TableData(data=layer_result, name="layer_result", fields_meta=layer_fields_meta)
        result_tables.add_table(total_table)
        result_tables.add_table(layer_table)

        self._ports_out["OutputResult"].data = result_tables
        return result_tables

    @staticmethod
    def __populate_total_result(total_result: dict[str, list], safety_factor: float, pile_length: float, profile_nums: list[str],
                           all_skin_capacity: list[dict], all_base_capacity: list[dict]) -> None:
        """Populate the total result dictionary with calculated values.
        
        Parameters
        ----------
        total_result : dict[str, list]
            Dictionary to store the results. The keys determine what values to calculate.

        safety_factor: float
            safety factor for calculation

        pile_length: float
            The pile length.

        profile_nums: list[str]
            The number of all the profiles.

        all_skin_capacity: list[dict]
            The skin capacity of all the profiles.

        all_base_capacity: list[dict]
            The base capacity of all the profiles.
        """
        for profile_num, skin_capacity, base_capacity in zip(profile_nums, all_skin_capacity, all_base_capacity):
            # capacity result
            total_skin_capacity = skin_capacity["total"]
            total_base_capacity = base_capacity["total"]
            
            # Populate the total_result dictionary based on available keys
            if GeoResults.ProfileNumber in total_result:
                total_result[GeoResults.ProfileNumber].append(profile_num)
            if GeoResults.PileLength in total_result:
                total_result[GeoResults.PileLength].append(pile_length)
            if GeoResults.PileBearingCapacity in total_result:
                total_result[GeoResults.PileBearingCapacity].append(total_skin_capacity + total_base_capacity)
            if GeoResults.SF in total_result:
                total_result[GeoResults.SF].append(safety_factor)
            if GeoResults.PileCharacteristicBearingCapacity in total_result:
                charac_bearing_capacity = (total_skin_capacity + total_base_capacity) / safety_factor
                total_result[GeoResults.PileCharacteristicBearingCapacity].append(charac_bearing_capacity)
            # additional result for steel pipe pile and concrete hollow pile
            if GeoResults.PileBaseBlockingFactor in total_result:
                total_result[GeoResults.PileBaseBlockingFactor].append(base_capacity["lambda_p"])
            # additiona result for concrete hollow pile
            if GeoResults.HollowPileNetArea in total_result:
                total_result[GeoResults.HollowPileNetArea].append(base_capacity["net_area"])
            if GeoResults.HollowPileOpenArea in total_result:
                total_result[GeoResults.HollowPileOpenArea].append(base_capacity["open_area"])
            # additional result for rock socketed pile
            if GeoResults.RockPileDepthToDiameter in total_result:
                total_result[GeoResults.RockPileDepthToDiameter].append(base_capacity["hr_d"])
            if GeoResults.RockPileResistanceFactor in total_result:
                total_result[GeoResults.RockPileResistanceFactor].append(base_capacity["zeta_r"])

    
    @staticmethod
    def __populate_layer_result(layer_result: dict[str, list], material_table: PortTypeHint.MaterialTable,
                                profile_nums: list[str], all_skin_capacity: list[dict], all_base_capacity: list[dict]) -> None:
        """Populate the layer result dictionary with calculated values.
        
        Parameters
        ----------
        layer_result: dict[str, list]
            Dictionary to store the layer results.

        material_table: PortTypeHint.MaterialTable
            The material table.

        profile_nums: list[str]
            The number of all the profiles.

        all_skin_capacity: list[dict]
            The skin capacity of all the profiles.

        all_base_capacity: list[dict]
            The base capacity of all the profiles.
        """
        for profile_num, skin_capacity, base_capacity in zip(profile_nums, all_skin_capacity, all_base_capacity):
            materials_id = skin_capacity["material_id"]
            layers_skin_friction = skin_capacity["per_layer"]
            layers_thickness = skin_capacity["thickness"]
            base_materail_id = base_capacity["material_id"]
            base_resistance = base_capacity["base_resistance"]
            # Additional result for post-grouted-pile
            if GeoMaterialProps.GroutedSkinFrictionFactor in layer_result:
                beta_si_list = skin_capacity["beta_si"]
            if GeoMaterialProps.GroutedBaseResistanceFactor in layer_result:
                beta_p = base_capacity["beta_p"]

            idx = 0
            for material_id, skin_friction, layer_thickness in zip(materials_id, layers_skin_friction, layers_thickness):
                if GeoResults.ProfileNumber in layer_result:
                    layer_result[GeoResults.ProfileNumber].append(profile_num)
                if GeoMaterialProps.LayerNumber in layer_result:
                    layer_number = material_table.get_material(material_id)[GeoMaterialProps.LayerNumber]
                    layer_result[GeoMaterialProps.LayerNumber].append(layer_number)
                if GeoMaterialProps.LimitSkinFriction in layer_result:
                    layer_result[GeoMaterialProps.LimitSkinFriction].append(skin_friction)
                if GeoMaterialProps.LimitBaseResistance in layer_result:
                    if material_id != base_materail_id:
                        layer_result[GeoMaterialProps.LimitBaseResistance].append(np.nan)
                    else:
                        layer_result[GeoMaterialProps.LimitBaseResistance].append(base_resistance)       
                if GeoResults.LayerThickness in layer_result:
                    layer_result[GeoResults.LayerThickness].append(layer_thickness)
                # Additional result for post-grouted-pile
                if GeoMaterialProps.GroutedSkinFrictionFactor in layer_result:
                    layer_result[GeoMaterialProps.GroutedSkinFrictionFactor].append(beta_si_list[idx])
                if GeoMaterialProps.GroutedBaseResistanceFactor in layer_result:
                    if material_id != base_materail_id:
                        layer_result[GeoMaterialProps.GroutedBaseResistanceFactor].append(np.nan)
                    else:
                        layer_result[GeoMaterialProps.GroutedBaseResistanceFactor].append(beta_p)
                # Additional result for rock socketed pile
                if GeoMaterialProps.SaturateUCS in layer_result:
                    layer_result[GeoMaterialProps.SaturateUCS].append(np.nan)

                idx += 1

            # Additional result for rock socketed pile
            if GeoMaterialProps.SaturateUCS in layer_result:
                f_rk = base_capacity["base_resistance"]
                base_layer_thickness = base_capacity["thickness"]
                layer_number = material_table.get_material(base_materail_id)[GeoMaterialProps.LayerNumber]
                layer_result[GeoMaterialProps.LayerNumber].append(layer_number)
                layer_result[GeoMaterialProps.SaturateUCS].append(f_rk)
                layer_result[GeoResults.ProfileNumber].append(profile_num)
                layer_result[GeoResults.LayerThickness].append(base_layer_thickness)
                layer_result[GeoMaterialProps.LimitSkinFriction].append(np.nan)


    @staticmethod
    def __get_total_fields_meta() -> list[FieldMetadata]:
        total_fields_meta = [
                FieldMetadata(name=GeoResults.ProfileNumber,
                              title="钻孔编号",
                              unit=Units.UNITLESS),
                FieldMetadata(name=GeoResults.PileLength,
                              title=GeoResults.PileLength.title,
                              unit=Units.m),
                FieldMetadata(name=GeoResults.PileBearingCapacity,
                              title=GeoResults.PileBearingCapacity.title,
                              unit=Units.kN),
                FieldMetadata(name=GeoResults.SF,
                              title=GeoResults.SF.title,
                              unit=Units.UNITLESS),
                FieldMetadata(name=GeoResults.PileCharacteristicBearingCapacity,
                              title=GeoResults.PileCharacteristicBearingCapacity.title,
                              unit=Units.kN),
                ]
        return total_fields_meta

    @staticmethod
    def __get_layer_fields_meta() -> list[FieldMetadata]:
        layer_fields_meta = [
                FieldMetadata(name=GeoResults.ProfileNumber,
                              title="钻孔编号",
                              unit=Units.UNITLESS),
                FieldMetadata(name=GeoMaterialProps.LayerNumber,
                              title=GeoMaterialProps.LayerNumber.title,
                              unit=Units.UNITLESS),
                FieldMetadata(name=GeoMaterialProps.LimitSkinFriction,
                              title=GeoMaterialProps.LimitSkinFriction.title,
                              unit=Units.kPa),
                FieldMetadata(name=GeoMaterialProps.LimitBaseResistance,
                              title=GeoMaterialProps.LimitBaseResistance.title,
                              unit=Units.kPa),
                FieldMetadata(name=GeoResults.LayerThickness,
                              title=GeoResults.LayerThickness.title,
                              unit=Units.m),
                ]
        return layer_fields_meta


@status_manage
class CompositePileCapacity(PipeModule):
    """Calculate the capacity of a composite pile"""

    def __init__(self, mname: str = "CompositePileCapacity", auto_run: bool = True,
                 profiles: PortTypeHint.MultiProfile1D | None = None,
                 material_table: PortTypeHint.MaterialTable | None = None,
                 pile: PortTypeHint.CompositePile | None = None) -> None:
        """Calculate the capacity of a composite pile.
        
        Parameters
        ----------
        profiles : PortTypeHint.MultiProfile1D, default: None
            The 1D geological profiles.
            It's necessary for calculating the capacity of a rigid pile.

        material_table : PortTypeHint.MaterialTable, default: None
            The material table.
            It's necessary for calculating the capacity of a rigid pile.

        pile : PortTypeHint.CompositePile, default: None
            The input composite pile.
        """
        super().__init__(mname=mname, auto_run=auto_run)

        self._ports_in = {"InputProfiles": Port(ptype=PortType.MultiProfile1D, data=profiles, pdoc="The 1D geological profiles."),
                          "InputMaterialTable": Port(ptype=PortType.MaterialTable, data=material_table, pdoc="The material table."),
                          "InputPile": Port(ptype=PortType.CompositePile, data=pile, pdoc="The input composite pile.")}
        self._ports_out = {"OutputResult": Port(ptype=[PortType.TableData, PortType.SingleResult], pdoc="The output composite pile capacity result.")}

    @property
    def InputProfiles(self) -> None:
        raise AttributeError("Property 'InputProfiles' is write-only.")
    
    @InputProfiles.setter
    def InputProfiles(self, value: PortTypeHint.MultiProfile1D | None) -> None:
        self["InputProfiles"] = value
    
    @property
    def InputMaterialTable(self) -> None:
        raise AttributeError("Property 'InputMaterialTable' is write-only.")
    
    @InputMaterialTable.setter
    def InputMaterialTable(self, value: PortTypeHint.MaterialTable | None) -> None:
        self["InputMaterialTable"] = value
    
    @property
    def InputPile(self) -> None:
        raise AttributeError("Property 'InputPile' is write-only.")
    
    @InputPile.setter
    def InputPile(self, value: PortTypeHint.CompositePile | None) -> None:
        self["InputPile"] = value
    
    @property
    def OutputResult(self) -> PortTypeHint.TableData | None:
        return self["OutputResult"]

    def set_cal_params(self, reset: bool = True) -> None:
        return None
    
    def execute(self) -> PortTypeHint.TableData | None:
        profiles: PortTypeHint.MultiProfile1D | None = self._ports_in["InputProfiles"].data
        material_table: PortTypeHint.MaterialTable | None = self._ports_in["InputMaterialTable"].data
        pile: PortTypeHint.CompositePile | None = self._ports_in["InputPile"].data
        
        if pile is None:
            self._ports_out["OutputResult"].data = None
            return None

        if pile.pile_type == PileType.RigidPile:
           if profiles is None or material_table is None:
               self._ports_out["OutputResult"].data = None
               return None
        
        if pile.pile_type == PileType.FlexiblePile:
            pile_capacity_cal = CompositePileCapacityCal(pile=pile)
            result = pile_capacity_cal.flexible_pile_capacity()

            if pile.pile_layout == "triangle" or pile.pile_layout == "square":
                pile_space = pile.pile_space
                lateral_space = None
                longitudinal_space = None
            elif pile.pile_layout == "rectangular":
                pile_space = None
                lateral_space = pile.lateral_space
                longitudinal_space = pile.longitudinal_space
            else:
                pile_space = None
                lateral_space = None
                longitudinal_space = None

            output = SingleResult([
                UnitResult(name=GeoResults.PileDiameter, title=GeoResults.PileDiameter.title, unit=Units.m, value=pile.diameter),
                UnitResult(name=GeoResults.PileLayout, title=GeoResults.PileLayout.title, value=pile.pile_layout_title_map[pile.pile_layout]),
                UnitResult(name=GeoResults.PileSpacing, title=GeoResults.PileSpacing.title, unit=Units.m, value=pile_space),
                UnitResult(name=GeoResults.LongitudinalSpacing, title=GeoResults.LongitudinalSpacing.title, unit=Units.m, value=longitudinal_space),
                UnitResult(name=GeoResults.LateralSpacing, title=GeoResults.LateralSpacing.title, unit=Units.m, value=lateral_space),
                UnitResult(name=GeoResults.AreaReplaceRatio, title=GeoResults.AreaReplaceRatio.title, value=pile.area_replace_ratio),
                UnitResult(name=GeoResults.CompositePileCharacteristicBearingCapacity, 
                           title=GeoResults.CompositePileCharacteristicBearingCapacity.title, 
                           unit=Units.kPa, value=result["total"])
            ])
            self._ports_out["OutputResult"].data = output
            return output

        bore_num = []
        base_layer_number = []
        base_layer_name = []
        pile_length = []
        pile_space = []
        longitudinal_space = []
        lateral_space = []
        pile_diameter = []
        pile_layout = []
        area_replace_ratio = []
        single_pile_capacity = []
        total_capacity = []
        for profile in profiles:
            bore_num.append(profile.pnum)
            if pile.pile_layout == "triangle" or pile.pile_layout == "square":
                pile_space.append(pile.pile_space)
                lateral_space.append(None)
                longitudinal_space.append(None)
            elif pile.pile_layout == "rectangular":
                pile_space.append(None)
                lateral_space.append(pile.lateral_space)
                longitudinal_space.append(pile.longitudinal_space)
            pile_length.append(pile.length)
            pile_diameter.append(pile.diameter)
            pile_layout.append(pile.pile_layout_title_map[pile.pile_layout])
            area_replace_ratio.append(pile.area_replace_ratio)
            # Calculate the capacity of the rigid pile
            pile_capacity_cal = CompositePileCapacityCal(pile=pile, profile=profile, material_table=material_table)
            result = pile_capacity_cal.rigid_pile_capacity()

            base_layer_material = material_table.get_material(result["base_soil"])
            base_layer_number.append(base_layer_material[GeoMaterialProps.LayerNumber])
            base_layer_name.append(base_layer_material[GeoMaterialProps.MaterialName])
            single_pile_capacity.append(result["single_pile"])
            total_capacity.append(result["total"])

        result_table = pd.DataFrame(
            data={
                GeoResults.BoreNumber: bore_num,
                GeoMaterialProps.LayerNumber: base_layer_number,
                GeoMaterialProps.LayerName: base_layer_name,
                GeoResults.PileLength: pile_length,
                GeoResults.PileDiameter: pile_diameter,
                GeoResults.PileLayout: pile_layout,
                GeoResults.PileSpacing: pile_space,
                GeoResults.LongitudinalSpacing: longitudinal_space,
                GeoResults.LateralSpacing: lateral_space,
                GeoResults.AreaReplaceRatio: area_replace_ratio,
                GeoResults.CompositeSinglePileBearingCapacity: single_pile_capacity,
                GeoResults.CompositePileCharacteristicBearingCapacity: total_capacity
            })

        fileds_metadata = [
            FieldMetadata(name=GeoResults.BoreNumber, title=GeoResults.BoreNumber.title),
            FieldMetadata(name=GeoMaterialProps.LayerNumber, title="持力层地层编号"),
            FieldMetadata(name=GeoMaterialProps.LayerName, title="持力层岩土名称"),
            FieldMetadata(name=GeoResults.PileLength, title=GeoResults.PileLength.title, unit=Units.m),
            FieldMetadata(name=GeoResults.PileDiameter, title=GeoResults.PileDiameter.title, unit=Units.m),
            FieldMetadata(name=GeoResults.PileLayout, title=GeoResults.PileLayout.title),
            FieldMetadata(name=GeoResults.PileSpacing, title=GeoResults.PileSpacing.title, unit=Units.m),
            FieldMetadata(name=GeoResults.LongitudinalSpacing, title=GeoResults.LongitudinalSpacing.title, unit=Units.m),
            FieldMetadata(name=GeoResults.LateralSpacing, title=GeoResults.LateralSpacing.title, unit=Units.m),
            FieldMetadata(name=GeoResults.AreaReplaceRatio, title=GeoResults.AreaReplaceRatio.title),
            FieldMetadata(name=GeoResults.CompositeSinglePileBearingCapacity, title=GeoResults.CompositeSinglePileBearingCapacity.title, unit=Units.kN),
            FieldMetadata(name=GeoResults.CompositePileCharacteristicBearingCapacity, title=GeoResults.CompositePileCharacteristicBearingCapacity.title, unit=Units.kPa),
        ]
        
        output = TableData(
            data=result_table,
            name="rigid_composite_pile",
            title="刚性桩复合地基",
            fields_meta=fileds_metadata
        )
        self._ports_out["OutputResult"].data = output
        return output

